package cz.vse.adventura.po1715.xforv00_forejt.gui._02_panely;

import cz.pecinovsky.english.pojj.adv_framework.game_gui.IGameG;
import cz.pecinovsky.english.pojj.adv_framework.game_gui.IUIG;
import cz.pecinovsky.english.pojj.adv_framework.game_txt.IGame;
import cz.pecinovsky.english.pojj.adv_framework.game_txt.IObject;
import cz.pecinovsky.english.pojj.adv_framework.game_txt.IPlace;
import cz.pecinovsky.english.pojj.adv_framework.test_util._Test_101;
import cz.vse.adventura.po1715.xforv00_forejt.hra.Hra;
import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import java.awt.event.ActionEvent;
import java.util.Collection;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;


/*******************************************************************************
 * Instance třídy {@code GUI_02} představují
 * grafické uživatelsé rozhraní umožňující hrát hry
 * vyhovující podmínkám rámce (frameworku) definovaného v balíčku
 * {@code cz.pecinovsky.english.pojj.adv_framework.game_txt},
 * tj. textové hry vyhovující požadavkům minulého semestru.
 *
 * @author    Vladimír FOREJT
 * @version   0.00.000
 */
public class GUI_02 implements IUIG
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
    /** Autor a XNAME */
    private static final String author = "FOREJT Vladimír";
    private static final String xname = "XFORV00";

    /** Souřadnice, na které se aplikační okno přesune po spuštění GUI_02.
     *  To má smysl především při zobrazování na sekundárním monitoru. */
    private static final Point POINT_0 = new Point(0, 0);   //-300, 0);

    /** Konstanty pro formátování výstupu - před a za příkazem*/
    private static final String
            BEFORE_COMMAND = "\n========================================================================\n",
            AFTER_COMMAND =  "\n------------------------------------------------------------------------\n";

    /** Odstup mezi jednotlivými prvky ve svisle uspořádaném panelu. */
    private static final int SVISLÝ_ODSTUP = 10;

//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** Hlavní okno GUI_02 */
    private final JFrame appWindow = new JFrame("První okno");

    /** Vstupní pole, kam uživatel zadává příkazy */
    private final JTextField inputField = new JTextField(25);

    /** Výstupní okno, kde hra vypisuje odpověď na uživatelovy příkazy */
    private final JTextArea outputArea = new JTextArea(30,45);

    /** Posluchač vstupního pole, který reaguje na uživatelovy příkazy
    *    a na příkazy zadané přes metodu {@link #executeCommand(String) }*/
    private final ActionListener inputListener = new TxfListener();

    /** Panel zborazující sousedy prostoru, v němž se hráč nachází. */
    private final Box neighborPane = new Box(BoxLayout.Y_AXIS);

    /** Návěští v panelu sousedů specifikující obsah daného panelu. */
    private final JLabel neighborTitle = new JLabel("===== Sousedé =====");

    /** Panel zborazující předměty v prostoru, v němž se hráč nachází. */
    private final Box objectPane = new Box(BoxLayout.Y_AXIS);

    /** Návěští v panelu objektů specifikující obsah daného panelu. */
    private final JLabel objectTitle = new JLabel("===== Předměty =====");

    /** Panel zborazující předměty v batohu */
    private final Box bagPane = new Box(BoxLayout.X_AXIS);

    /** Návěští v panelu batohu */
    private final JLabel bagTitle = new JLabel("===== Batoh =====");

//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Hra, která se bude hrát pomocí tohoto GUI_02 */
    private IGame thisGame;

//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     *Vytvoří instanci GUI_02, které bude komunikovat s hrou
     * zadanou v parametru
     *
     * Co se týká celého okna, zůstává v konstruktoru, co se týká částí,
     *      přesouvám do příslušných metod, které se o ně starají
     * @param game Hra, která se použije
     */
    public GUI_02()
    {
        prepareOutputArea();
        prepareInputField();
        prepareNeighborPane();
        prepareObjectPane();
        prepareBagPane();

        /** Co se stane, když uživatel zavře okno
         * EXIT_ON_CLOSE ukončí aplikaci */
        appWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        /** Nastaví oknu nejmenší možnou velikost
         *(v závislosti na jeho komponentách */
        appWindow.pack();

        //Zviditelní okno
        appWindow.setVisible(true);

        
        
       

        /** Zadá hře prázdný příkaz a vypíše odpověď */
        //String response = game.executeCommand("");
        //outputArea.setText(response + "\n");

//        //Následující posunutí má smysl pouze při zobrazování
        //        //na druhém monitoru, který je umístněn vlevo od hlavního
        //        appWindow.setLocation(POINT_0);
        
    }





//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    /***************************************************************************
     * Vrátí jméno autora scénáře ve formátu <b>PŘÍJMENÍ Křestní</b>,
     * tj. neprve příjmení psané velkými písmeny a za ním křestní jméno,
     * u nějž bude velké pouze první písmeno a ostatní písmena budou malá.
     *
     * @return Jméno autora/autorky programu ve tvaru PŘÍJMENÍ Křestní
     */
    @Override
    public String getAuthor()
    {
        return author;
    }


    /***************************************************************************
     * Vrátí x-name autora/autorky programu zapsané velkými písmeny.<p>
     * X-name má tvar <b>{@code XPPPK99}</b> nebo <b>{@code XPPPK999}</b>,
     * kde<br>
     * <ul>
     *   <li><b>{@code PPP}</b> zastupuje první tří písmena příjmení autora(ky)
     *      zapsaná velkými písmeny, </li>
     *   <li><b>{@code K}</b> zastupuje první písmeno křestního jména a </li>
     *   <li><b>{@code 99}</b>, resp. <b>{@code 999}</b> zastupují číslice
     *      přiřazené systémem, pomocí nichž se rozsliší studenti(ky)
     *      s předchozími znaky shodnými.
     * </ul>
     *
     * @return X-name autora/autorky programu
     */
    @Override
    public String getXNAME()
    {
        return xname;
    }


    /***************************************************************************
     * Spustí komunikaci mezi zadanou hrou a danou instancí GUI_02
     * mající na starosti komunikaci s uživatelem.
     * <p>
     * Metoda před pokusem o spuštění zadané hry nejprve zkontroluje,
     * že pomocí daného GUI_02 není akutálně hrána žádná hra
     * a že ani zadaná hra není rozehrána. V opačném případě vyhodí výjimku.
     * <p>
     * Tato verze ještě není plnohodntou implementací,
     * protože je schopna obsluhovat i hry implementující rozhraní
     * a proto není pravda, že: "<i>nejprve však zkontroluje,
     * že zadaná hra je ve skutečnosti instancí rozhraní {@link IGameG},
     * protože jiné neposkytují metody, které grafické UI očekává</i>".
     *
     * @param game Hra, kterou ma dané UI spustit
     * @throws IllegalArgumentException
     *         Existují důvody, které brání spuštění zadané hry
     */
    @Override
    public void startGame(IGame game)
    {
        if ((game == null) || (! game.isReady())) {
            throw new RuntimeException("Zadaná hra je prázdná nebo ještě běží");
        }
        /** Kontrola, jestli není právě rozehraná hra */
        if ((thisGame != null) && (! thisGame.isReady())) {
            int answer = JOptionPane.showConfirmDialog(appWindow,
                    "V současné době je rozehrána hra\n\n" + thisGame.getClass() +
                    "Opravdu chcete tuto hru ukončit a začít hrát hru\n\n " +
                    game.getClass());
            /** Když stiskne YES, pokračuje (ukončí hranou hru) */
            if (answer != 0) {
                return;
            }
        }
        thisGame = game;
        /** GUI_02 může mít rozmístěné komponenty ze staré hry 
         * -> inicializovat pro zadanou hru*/
        initialize();

        executeCommand("");     //Odstartuje zadáním prázdného příkazu

        
    }


    /***************************************************************************
     * Spustí komunikaci mezi implicitní hrou a danou instancí
     * mající na starosti komunikaci s uživatelem.
     */
    @Override
    public void startGame()
    {
        IGame choosedGame = Hra.getHra();
        startGame(choosedGame);
    }


    /***************************************************************************
     * Zpracuje příkaz předaný v parametru jako by byl zadán standardním
     * postupem z klávesnice. Metoda umožňuje testovat reakce daného
     * uživatelského rozhraní na příkazy zadávané z klávesnice.
     *
     * @param command Zadávaný příkaz
     */
    @Override
    public void executeCommand(String command)
    {
        //Zadá obdržený příkaz do vstupního pole jako zadaný text
        inputField.setText(command);

        //Zavolá metodu posluchače čekajícího na zadaní příkazu hry
        //Tato metoda zpracuje příkaz jako kdyby jej zadal hráč
        inputListener.actionPerformed(null);
    }





//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================
//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
    /***************************************************************************
     * Inicializuje GUI (aplikační okno) tak, aby se mohla začít hrát nová hra.
     */
    private void initialize() {
        /**Nejdřív jednotlié oblasti */
        initializeOutputArea();
        initializeInputField();
        initializeNeighborPane();
        initializeObjectPane();
        initializeBagPane();
       
    }
    /***************************************************************************
     * Inicializuje oblast s vystupujícím textem.
     * Zde je potřeba při každém spuštění hry smazat
     * případný dříve vypsaný text, aby hra začala s čistou oblastí.
     */
    private void initializeOutputArea() {
        outputArea.setText("");
    }

    /***************************************************************************
     * Inicializuje oblast zobrazující vstupní textové pole.
     */
    private void initializeInputField() {
        //Nepotřebuje inicializaci
    }
    
    /***************************************************************************
     * Inicializuje oblast zobrazující sousední místnosti
     */
    private void initializeNeighborPane() {
        //Nepotřebuje inicializaci
    }

    /***************************************************************************
     * Inicializuje oblast zobrazující předměty v místnosti
     */
    private void initializeObjectPane() {
        //Nepotřebuje inicializaci
    }

    /***************************************************************************
     * Inicializuje oblast zobrazující předměty v batohu
     */
    private void initializeBagPane() {
        //Inicializace není potřeba
    }

    /***************************************************************************
    *Připraví výstupní pole, které informuje o stavu hry
    */
     private void prepareOutputArea() {
         /** Zakáže měnit text ve výstupní oblasti */
        outputArea.setEditable(false);

        //Vytvoří dekorátor, který zařídí zobrazení posuvníků v případě potřeby
        JScrollPane scroller = new JScrollPane(outputArea);

        /** Přidá do okna dekorátor scroller
         *  BorderLayout - implicitní správce rozvržení */
        appWindow.add(scroller, BorderLayout.CENTER);
    }

    /***************************************************************************
    *Připraví vstupní pole, do kterého uživatel zadává příkazy
    */
    private void prepareInputField() {
         /** Posluchač, který čeká na stisk ENTER
         * a zpracovává příkazy ve vstupním poli */
        inputField.addActionListener(inputListener);

        JPanel inputPane = new JPanel();
        JLabel prompt = new JLabel("Zadej příkaz: ");

        inputPane.add(prompt);
        inputPane.add(inputField);

        appWindow.add(inputPane, BorderLayout.SOUTH);
    }

/***************************************************************************
*Připraví panel, kde se zobrazují sousední místnosti
*/
    private void prepareNeighborPane() {
        appWindow.add(neighborPane, BorderLayout.WEST);
    }
    /***************************************************************************
    *Připraví panel, kde se zobrazují předměty v místnosti
    */
    private void prepareObjectPane() {
        appWindow.add(objectPane, BorderLayout.EAST);
    }

    /***************************************************************************
    *Připraví panel, kde se zobrazuje obsah batohu
    */
    private void prepareBagPane() {
        appWindow.add(bagPane, BorderLayout.NORTH);
    }

     /***************************************************************************
     * Aktualizuje oblast zobrazující vstupní textové pole.
     * V této verzi stačí vstupní pole vyčistit a připravit pro další vstup.
     */
    private void updateInputField() {
        inputField.setText("");
        inputField.requestFocus();
    }

    /***************************************************************************
     * Aktualizuje oblast s vystupujícím textem, tj.
     * přečte si zadaný příkaz a zapíše jej do výstupní oblasti,
     * zjistí, jak na tento příkaz hra zareaguje
     * a zapíše její odpověď do výstupní oblasti.
     */
    private void updateOutputArea() {
        String command = inputField.getText();
        outputArea.append(BEFORE_COMMAND + command + AFTER_COMMAND);
        String answer = thisGame.executeCommand(command);
        outputArea.append(answer + "\n");
        outputArea.setCaretPosition(outputArea.getText().length());
    }

   /***************************************************************************
     * Aktualizuje oblast zobrazující sousedy aktuálního prostoru.
     */
    private void updateNeighborPane() {
            //Nejprve vyčistí kontejner
            neighborPane.removeAll();

            //Přidá do něj znovu titulek
            neighborPane.add(neighborTitle);

            //Zjistí od hry, který prostor je aktuální ...
            IPlace actualPlace = thisGame.getActualPlace();

            //... a od něj získá kolekci jeho sousedů
            Collection<? extends IPlace> neighbors = actualPlace.getNeighbors();

            //"Oběhne" všechny zjištěné sousedy
            for (IPlace place:neighbors) {
                //Odsadí jej od předchozí položky...
                neighborPane.add(Box.createVerticalStrut(SVISLÝ_ODSTUP));

                //... a přidá do kontejneru komponentu zobrazující jeho název
                String neighborName = place.getName();
                JLabel placeLabel = new JLabel(neighborName);
                neighborPane.add(placeLabel);
            }
            appWindow.repaint();    //Zapamatuje si, že se musí překreslit...
            appWindow.validate();   //... a upozorní na změnu svůj kontejner
        }


    /***************************************************************************
     * Aktualizuje oblast zobrazující objekty v aktuálním prostoru.
     */
     private void updateObjectPane() {
            objectPane.removeAll();
            objectPane.add(objectTitle);

            IPlace actualPlace = thisGame.getActualPlace();
            Collection<? extends IObject> objects = actualPlace.getObjects();
            for (IObject object:objects) {
                objectPane.add(Box.createVerticalStrut(SVISLÝ_ODSTUP));
                String objectName = object.getName();
                JLabel objectLabel = new JLabel(objectName);
                objectPane.add(objectLabel);
            }

            appWindow.repaint();
            appWindow.validate();
     }

     /***************************************************************************
     * Aktualizuje oblast zobrazující obsah batohu
     */
     private void updateBagPane() {
            bagPane.removeAll();
            bagPane.add(bagTitle);

            Collection<? extends IObject> objects = thisGame.getBag().getObjects();
            for(IObject object:objects) {
                bagPane.add(Box.createHorizontalStrut(SVISLÝ_ODSTUP));
                String objectName = object.getName();
                JLabel objectLabel = new JLabel(objectName);
                bagPane.add(objectLabel);
            }
            appWindow.repaint();
            appWindow.validate();
     }





//== INTERNÍ DATOVÉ TYPY =======================================================
    /***************************************************************************
     * Vnitřní třída, jejíž instance představují posluchače
     * vstupního textového pole čekající na stisk klávesy {@code Enter}
     * v okamžiku, kdy má pole fokus.
     */
    private class TxfListener implements ActionListener
    {
        /***********************************************************************
         * Zjistí aktuální text ve vstupním poli (= text zadaného příkazu),
         * zapíše jej do výstupní oblasti,
         * předá zadaný příkaz obsluhované hře ke zpracování
         * a do výstupního pole zapíše obdržený výsledek.
         *
         * @param e Objekt, který je schopen poskytnout informace o události,
         *          avšak v této verzi není využíván
         */
        @Override
        public void actionPerformed(ActionEvent e)
        {
            //Začneme aktualizací vystupní oblasti, protože zadává příkaz hře
            updateOutputArea();

            updateNeighborPane();
            updateObjectPane();
            updateBagPane();
            
            //Skončíme aktualizací vstupní oblasti,
            //protože se při ní definitivně zapomíná zadaný příkaz
            updateInputField();
        }


    }
//== TESTY A METODA MAIN =======================================================
//
//    /***************************************************************************
//     * Testovací metoda.
//     */
//    public static void test()
//    {
//        GUI_02 inst = new GUI_02();
//    }
//    /** @param args Parametry příkazového řádku - nepoužívané. */
//    public static void main( String[] args )  {  test();  }
    /***************************************************************************
     * Testovací metoda předvede hraní implicitní hry
     * prostřednictvím gui - instance této třídy.
     */
    public static void test()
    {
        /**GUI_02   gui   = new GUI_02(null);
        IGame    gameX = _Test_101.getDefaultGame();
        _Test_101.playGameByScenario(gameX, 2, gui);*/
         IGame  gameX = Hra.getHra();
        GUI_02 gui   = new GUI_02();
        _Test_101.playGameByScenario(gameX, 2, gui);
    }
    /** @param args Parametry příkazového řádku - nepoužívané. */
    public static void main( String[] args )  {  test();  }
}
